package com.lyfam.velcro.tableTag;

import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;

import ognl.Ognl;

import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;

import com.lyfam.velcro.common.impl.AbstractHtmlElement;
import com.lyfam.velcro.common.impl.SimpleValueHtmlElement;

public class TableTag extends SimpleTagSupport
{
	// Hold the columns for this table, primarily for rendering the
	// table header.
	private SortedMap<String, AbstractHtmlElement> columns = new TreeMap<String, AbstractHtmlElement>();

	/**
	 * Hold the rows of the table. Each row is a list of cell element
	 */
	private List<List<AbstractHtmlElement>> rows = new ArrayList<List<AbstractHtmlElement>>(100);

	/**
	 * Attributes of tag
	 * 
	 */
	private String tableId;
	private String className;
	private String rawHtml;
	private String rowDataVarName;
	private Object data;

	public AbstractHtmlElement getColumnById(String id)
	{
		return columns.get(id);
	}

	public List<AbstractHtmlElement> getColumns()
	{
		return new ArrayList<AbstractHtmlElement>(columns.values());
	}

	public List<List<AbstractHtmlElement>> getRows()
	{
		return rows;
	}

	public void addColumn(AbstractHtmlElement col)
	{
		this.columns.put(col.getId(), col);
	}

	public String getClassName()
	{
		return className;
	}

	public void setClassName(String className)
	{
		this.className = className;
	}

	public String getRawHtml()
	{
		return rawHtml;
	}

	public void setRawHtml(String rawHtml)
	{
		this.rawHtml = rawHtml == null ? "" : rawHtml;
	}

	public String getTableId()
	{
		return tableId;
	}

	public void setTableId(String tableId)
	{
		this.tableId = tableId;
	}

	private static VelocityContext getVelocityContext() throws java.lang.Exception
	{
		Velocity.addProperty(Velocity.RESOURCE_LOADER, "classpath");
		Velocity.addProperty("classpath." + Velocity.RESOURCE_LOADER + ".class",
								"org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
		Velocity.setProperty(VelocityEngine.COUNTER_INITIAL_VALUE, new Integer(0));
		Velocity.init();

		return new VelocityContext();
	}

	public Collection<?> getDataCollection()
	{
		return (Collection<?>) data;
	}

	public void setData(Object data)
	{
	    if (data.getClass().isArray())
	    {
	        this.data = Arrays.asList((Object[]) data);
	    }
	    else if (data instanceof Collection<?>)
	    {
	        this.data = (Collection<?>) data;
	    }
	    else
	    {
	        throw new IllegalArgumentException("The data need to be array or collection type.");
	    }
	}
		
	@Override
	public void doTag() throws JspException, IOException
	{

		String templatepath = "com/lyfam/velcro/tableTag/resources/tableTemplate.html";

		try
		{
			VelocityContext ctx = getVelocityContext();

			for (Object rowData : getDataCollection())
			{
				getJspContext().setAttribute(getRowDataVarName(), rowData);

				JspFragment body = getJspBody();
				body.invoke(null);

				List<AbstractHtmlElement> aRow = new ArrayList<AbstractHtmlElement>(20);
				for (AbstractHtmlElement column : getColumns())
				{
					String itemRender = ((ColumnHtmlElement) column).getItemRenderer();
					String columnBody = ((ColumnHtmlElement) column).getBodyContent();

					AbstractHtmlElement cell = new SimpleValueHtmlElement();

					if (itemRender != null)
					{
						Class<IRenderer> rendererClass = (Class<IRenderer>) Class.forName(itemRender);
						IRenderer renderer = rendererClass.newInstance();
						cell.setValue(renderer.getRenderString(rowData));
					}
					else
					{
						if (columnBody != null)
						{
							cell.setValue(columnBody);
						}
						else
						{
							if (column.getDataField() == null)
							{
								cell.setValue(rowData.toString());
							}
							else
							{
								cell.setValue(Ognl.getValue(column.getDataField(), rowData).toString());
							}
						}
					}

					aRow.add(cell);
				}

				rows.add(aRow);
			}

			ctx.put("table", this);

			StringWriter myStringWriter = new StringWriter();
			Velocity.mergeTemplate(templatepath, org.apache.velocity.runtime.RuntimeSingleton
					.getString(Velocity.INPUT_ENCODING, Velocity.ENCODING_DEFAULT), ctx, myStringWriter);

			getJspContext().getOut().print(myStringWriter.toString());
		}
		catch (Exception e)
		{
			throw new JspException("Exception; TEMPLATE_CLASSPATH=" + templatepath + "; exception=" + e.getMessage());
		}
	}

    public String getRowDataVarName()
    {
        return rowDataVarName == null ? "rowData" : rowDataVarName;
    }

    public void setRowDataVarName(String rowDataVarName)
    {
        this.rowDataVarName = rowDataVarName == null ? "rowData" : rowDataVarName;
    }
}
